/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package model;

import ihm.PanelTraceExecution;
import java.util.ArrayList;
import java.util.List;
import util.Observable;
import util.Observeur;

/**
 *
 * @author benoit
 */
public class Ascenseur implements HistoriqueTrajet,Observable {
    /* Statuts des ascenseurs */
    public static final int ST_EN_ATTENTE=0;
    public static final int ST_EN_COURS_DE_TRAJET=2; // Rejoins l'étage de départ
    public static final int ST_INIT_TRAJET=6; // Rejoins l'étage d'arrivé
    public static final int ST_REPOSITIONNEMENT=7;
    public static final int ST_ARRIVE=3;
    public static final int ST_BLOQUE=4;
    public static final int ST_ARRET_D_URGENCE=5;
    
    /* Direction de l'ascenseur */
    public static final int MONTEE=100;
    public static final int IMMOBILE=150;
    public static final int DESCENTE=200;
    
    /* Consommation électrique des acenseur */
    public static final int CONSO_MONTEE=1500;
    public static final int CONSO_DESCENTE=750;
    
    /* Les vitesses des ascenseurs */
    public static final int VITESSE_0=9; // Immobile
    public static final int VITESSE_1=10; // 1 étage = 3s
    public static final int VITESSE_2=20; // 1 étage = 2s
    public static final int VITESSE_3=30; // 1 étage = 1s
    
    /* Constantes diverses de l'ascenseur */
    public static final int NB_PERSONNE_MAX=15;
    public static final int CHANCE_DE_TOMBER_EN_PANNE=1000; // il faut faire 1/CHANCE_DE_TOMBER_EN_PANNE
    
    private int numero;
    private int statut;
    private int nombrePersonne;
    private int energieConsommee;
    private Etage etageCourant;
    private Etage etageInitial;
    private boolean porteOuverte;
    private int directionActuel;
    private int vitesse;
    private Integer delaiAvantProchaineEtage; // La valeur 0 permet d'indiquer que l'on peut passer au prochaine étage
    private boolean surLeDepart; // Boolean qui indique si l'ascenseur s'apprête à partir de l'étage actuel

    private List<Trajet> trajetsEnCours; /* Personne dans l'ascenseur ou qui va être prise par l'ascenseur sous peu */
    private List<Trajet> trajetsEnAttente; /* Personne qui attendent dans l'ascenseur */
    private List<Trajet> trajetsEffectues;
    private List<Observeur> observeurs;

    public Ascenseur(int numero, Etage etageCourant, Etage etageInitial) {
        this.numero = numero;
        this.statut = ST_EN_ATTENTE;
        this.nombrePersonne = 0;
        this.etageCourant = etageCourant;
        this.etageInitial = etageInitial;
        this.porteOuverte = false;
        this.directionActuel = IMMOBILE;
        this.vitesse = VITESSE_0;
        this.trajetsEnCours = new ArrayList<Trajet>();
        this.trajetsEnAttente = new ArrayList<Trajet>();
        this.trajetsEffectues = new ArrayList<Trajet>();
        this.observeurs = new ArrayList<Observeur>();
        this.delaiAvantProchaineEtage = 0;
        this.energieConsommee = 0;
        this.surLeDepart = false;
    }
    
    @Override
    public List<Trajet> getTrajets() {
        return trajetsEffectues;
    }

    public int getNumero() {
        return numero;
    }

    public int getStatut() {
        return statut;
    }

    public void setStatut(int statut) {
        String str = "";
        this.statut = statut;
        notifierObserveurs();
        switch(statut) {
            case ST_ARRET_D_URGENCE:
                str = "/!\\ Arrêt d'urgence sur l'ascenseur n°" + numero;
                break;
            case ST_ARRIVE:
                str = "AS n°" + numero + " a fini de traiter ses trajets";
                break;
            case ST_BLOQUE:
                str = "/!\\ L'ascenseur n°" + numero + " est bloqué pour une raison inconnue";
                break;
            case ST_EN_ATTENTE:
                str = "AS n°" + numero + " est passé en attente";
                break;
            case ST_EN_COURS_DE_TRAJET:
                str = "AS n°" + numero + " est en cours de trajet";
                break;
            case ST_INIT_TRAJET:
                str = "AS n°" + numero + " initialise un trajet";
                break;
            case ST_REPOSITIONNEMENT:
                str = "AS n°" + numero + " va se repositionner à l'étage n°" + etageInitial;
                break;
        }
        PanelTraceExecution.ecrireTraceExecution(str);
    }

    public int getNombrePersonne() {
        return nombrePersonne;
    }
    
    public boolean incrementerNbPersonne(int n) {
        if(nombrePersonne + n <= NB_PERSONNE_MAX) {
            nombrePersonne += n;
            PanelTraceExecution.ecrireTraceExecution("AS n°" + numero + " a fait rentré "+n+" personne(s) => TOT = "+ nombrePersonne);
            return true;
        }
        return false;
    }
    
    public boolean decrementerNbPersonne(int n) {
        if(nombrePersonne - n >= 0) {
            nombrePersonne -= n;
            PanelTraceExecution.ecrireTraceExecution("AS n°" + numero + " a fait sortir "+n+" personne(s) => TOT = "+ nombrePersonne);
            return true;
        }
        return false;
    }

    public Etage getEtageCourant() {
        return etageCourant;
    }

    public void setEtageCourant(Etage etageCourant) {
        this.etageCourant = etageCourant;
        notifierObserveurs();
        PanelTraceExecution.ecrireTraceExecution("AS n°" + numero + " se déplace à l'étage n° " + etageCourant);
    }

    public Etage getEtageInitial() {
        return etageInitial;
    }

    public boolean isPorteOuverte() {
        return porteOuverte;
    }
    
    public boolean isSurLeDepart() {
        return surLeDepart;
    }
    
    public void setSurLeDepart(boolean b) {
        this.surLeDepart = b;
        notifierObserveurs();
        PanelTraceExecution.ecrireTraceExecution("AS n°" + numero + " est sur le départ");
    }

    public void setPorteOuverte(boolean porteOuverte) {
        this.porteOuverte = porteOuverte;
        notifierObserveurs();
        PanelTraceExecution.ecrireTraceExecution("AS n°" + numero + " => " +(porteOuverte ? "Les portes s'ouvrent" : "Les portes se ferment"));
    }

    public List<Trajet> getTrajetsEnAttente() {
        return trajetsEnAttente;
    }
    
    public int getDirectionActuel() {
        return directionActuel;
    }

    public List<Trajet> getTrajetsEnCours() {
        return trajetsEnCours;
    }
    
    public void setDirectionActuel(int directionActuel) {
        this.directionActuel = directionActuel;
        PanelTraceExecution.ecrireTraceExecution("AS n°" + numero + " => " + (directionActuel == IMMOBILE ? "Immobile" : (directionActuel == MONTEE ? "Monte" : "Descend" )));
    }

    public int getVitesse() {
        return vitesse;
    }

    public void setVitesse(int vitesse) {
        this.vitesse = vitesse;
        if(vitesse ==  VITESSE_0) {
            this.delaiAvantProchaineEtage = 0;
        } else if(vitesse == VITESSE_1) {
            this.delaiAvantProchaineEtage = 2;
        } else if(vitesse == VITESSE_2) {
            this.delaiAvantProchaineEtage = 1;
        } else if(vitesse == VITESSE_3) {
            this.delaiAvantProchaineEtage = 0;
        } else {
            throw new IllegalArgumentException("La vitesse n'est pas valide");
        }
    }

    public int getNbrEtagePourArretLePlusProche() {
        if(vitesse == VITESSE_0) {
            return 0;
        } else if(vitesse == VITESSE_1) {
            return 0;
        } else if(vitesse == VITESSE_2) {
            return 1;
        } else {
            return 2;
        }
    }
    
    public Integer getDelaiAvantProchaineEtage() {
        return delaiAvantProchaineEtage;
    }
    
    public void decrementerDelaiAvantProchaineEtage(){
        if(this.delaiAvantProchaineEtage>0) {
            this.delaiAvantProchaineEtage--;
        }
    }
    
    public void setDelaiAvantProchaineEtage(int n) {
        if(this.delaiAvantProchaineEtage>=0) {
            this.delaiAvantProchaineEtage = n;
        } else {
            throw new IllegalArgumentException("Le délai doit être > 0");
        }
    }
    
    public void incrementerEnergieConsommee(int n) {
        this.energieConsommee += n;
    }
    
    public void resetEnergieConsommee() {
        this.energieConsommee = 0;
    }
    
    public boolean isImmobile() {
        return this.directionActuel == IMMOBILE;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 61 * hash + this.numero;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Ascenseur other = (Ascenseur) obj;
        return this.numero == other.numero;
    }

    @Override
    public void ajouterObserveur(Observeur o) {
        if(o==null) {
            throw new IllegalArgumentException("Null pointer argument 1");
        }
        observeurs.add(o);
    }

    @Override
    public void supprimerObserveur(Observeur o) {
        observeurs.remove(o);
    }

    @Override
    public void notifierObserveurs() {
        for(Observeur o : observeurs) {
            o.update();
        }
    }

    @Override
    public String toString() {
        return "Ascenseur{\n\t" + "numero=" + numero + "\n\tstatut=" + statut + "\n\tsur le départ=" + surLeDepart + "\n\tnombrePersonne=" + nombrePersonne + "\n\tenergieConsommee=" + energieConsommee + "\n\tetageCourant=" + etageCourant + "\n\tetageInitial=" + etageInitial + "\n\tporteOuverte=" + porteOuverte + "\n\tdirectionActuel=" + (directionActuel == MONTEE ? "monte" : (directionActuel == DESCENTE ? "descend" : "immobile")) + "\n\tvitesse=" + vitesse + "\n\tdelaiAvantProchaineEtage=" + delaiAvantProchaineEtage + "\n\ttrajetsEnCours=" + trajetsEnCours + "\n\ttrajetsEnAttente=" + trajetsEnAttente + "\n\ttrajetsEffectues=" + trajetsEffectues + "\n\tobserveurs=" + observeurs + "\n}";
    }
}
